package com.xxw.springbootencrypt.interceptor;

import com.alibaba.fastjson.JSON;
import com.xxw.springbootencrypt.annotation.ResponseEncrypt;
import com.xxw.springbootencrypt.bean.Result;
import com.xxw.springbootencrypt.util.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/*
* 响应参数加密参数
* ResponseBodyAdvice可以理解为在@ResponseBody之后进行的操作，所以当接口需要加解密时，
* 在使用@RequestBody接收前台参数之前可以先在RequestBodyAdvice的实现类中进行参数的解密，
* 当操作结束需要返回数据时，可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。
* */
@Component
@ControllerAdvice(basePackages = "com.xxw.springbootencrypt.controller")
@Slf4j
public class EncryResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    RsaUtil rsaUtil;

    /*
     * supports 定义什么注解生效，默认 @ResponseBody
     * */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        // 设置带有 ResponseEncrypt 注解的才做加密.
        return methodParameter.hasMethodAnnotation(ResponseEncrypt.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //通过 ServerHttpRequest的实现类ServletServerHttpRequest 获得HttpServletRequest
        ServletServerHttpRequest sshr = (ServletServerHttpRequest) serverHttpRequest;
        //此处获取到request 是为了取到在拦截器里面设置的一个对象 是我项目需要,可以忽略
        HttpServletRequest request = sshr.getServletRequest();

        byte[] enData = null;
        try {
            //添加 encry header，告诉前端数据已加密
            serverHttpResponse.getHeaders().add("encry", "true");

            // 类型转换并获取响应的数据
            Result resultData = (Result)o;
            Map<String, Object> data = resultData.getData();

            // 转成字符串进行加密
            String srcDataStr = JSON.toJSONString(data);
            enData = rsaUtil.encryptData(srcDataStr.getBytes());
            log.info("接口={},原始数据={},加密后数据={}", request.getRequestURI(), srcDataStr, enData);

        } catch (Exception e) {
            log.error("异常！", e);
        }

        // 数据再封装一下
        Map<String, String> dataMap = new HashMap<>();
        dataMap.put("data",new String(enData));

        return dataMap;
    }
}

